package org.infoset.xml.memory;

import java.util.*;
import java.net.*;

import org.infoset.xml.*;



public class MemoryInfoset implements Infoset {

   static Infoset defaultInfoset = new MemoryInfoset(MemoryNameTable.getDefaultNameTable());

   public static Infoset getDefaultInfoset() {
      return defaultInfoset;
   }
   
   class MemoryItemConstructor implements ItemConstructor {
      URI base;
      MemoryInfoset context;
      Stack ancestors;
      
      MemoryItemConstructor(MemoryInfoset context, URI baseURI) {
         this.base = baseURI;
         this.context = context;
         this.ancestors = new Stack();
      }
      
      public Attribute createAttribute(Name name,String value) {
         return new MemoryAttribute(context,null,name,value,null);
      }
      
      /** Creates a parentless data item.
       * @param value The value for the data item.
       */
      public Characters createCharacters(String value) {
         return new MemoryCharacters(context,null,value,null);
      }
      
      /** Creates a parentless data item.
       * @param value The value for the data item.
       */
      public Characters createCharacters(char [] value,int start,int len) {
         return new MemoryCharacters(context,null,new String(value,start,len),null);
      }
      
      /** Creates a document without a document element or base URI.
       * @return A document instance.
       */
      public Document createDocument() {
         ancestors.clear();
         return new MemoryDocument(null,context);
      }
      
      /** Creates a document without a document element with the specified base URI.
       * @param base The base URI (e.g. location) of the document to create.
       * @return A document instance.
       */
      public Document createDocument(URI base) {
         ancestors.clear();
         this.base = base;
         return new MemoryDocument(base,context);
      }
      
      /** Creates a document end event.
       */
      public DocumentEnd createDocumentEnd() {
         ancestors.clear();
         return new MemoryDocumentEnd(context);
      }
      
      /** Creates a parentless element.
       * @param name The name of the element to create.
       * @return An element instance.
       */
      public Element createElement(Name name) {
         NamespaceScope lastScope;
         if (ancestors.isEmpty()) {
            lastScope = NamespaceScope.getDefaultScope();
         } else {
            lastScope = ((Element)ancestors.peek()).getNamespaceScope();
         }
         // TODO: handle base properly
         Element e = new MemoryElement(
            context,
            base, // base
            name,
            lastScope
         );
         ancestors.push(e);
         return e;
      }
      
      /** Creates an element end event.
       * @param the name of end element.
       * @param the scope of the start element.
       */
      public ElementEnd createElementEnd(Name name) 
         throws XMLException
      {
         if (ancestors.empty()) {
            throw new XMLException("ancestors not balanced: possible mixed-streams of item construction or mix");
         }
         NamespaceScope scope = ((Element)ancestors.pop()).getNamespaceScope();
         return new MemoryElementEnd(context,name,scope);
      }
      
      /** Creates an element end event.
       * @param the name of end element.
       * @param the scope of the start element.
       */
      public ElementEnd createElementEnd(Element startElement) 
         throws XMLException
      {
         NamespaceScope scope = startElement.getNamespaceScope();
         if (!ancestors.empty()) {
            scope = ((Element)ancestors.pop()).getNamespaceScope();
         }
         return new MemoryElementEnd(context,startElement.getName(),scope);
      }
      
      public Infoset getInfoset() {
         return context;
      }
      
      public Comment createComment(String value) {
         return new MemoryComment(context,null,value);
      }
      
      public Comment createComment(char[] value, int start, int len) {
         return new MemoryComment(context,null,new String(value,start,len));
      }
      
      public ProcessingInstruction createProcessingInstruction(Name name, String value) {
         return new MemoryProcessingInstruction(context,null,name,value);
      }
      
      public ProcessingInstruction createProcessingInstruction(Name name, char[] value, int start, int len) {
         return new MemoryProcessingInstruction(context,null,name,new String(value,start,len));
      }
      
      public Sequence createSequence()
         throws XMLException
      {
         return new MemorySequence(context);
      }

      public SequenceEnd createSequenceEnd()
         throws XMLException 
      {
         return new MemorySequenceEnd(context);
      }

      public Value createValue(Object value) {
         return new MemoryValue(context,value);
      }

      public Value createValue(Object value, String lexicalValue, TypeDefinition typeDef) {
         MemoryValue mvalue = new MemoryValue(context,value);
         mvalue.lexicalValue = lexicalValue;
         mvalue.setTypeDefinition(typeDef);
         return mvalue;
      }
   }

   NameTable shadowTable;

   public MemoryInfoset() {
      this(new MemoryNameTable());
   }

   public MemoryInfoset(NameTable shadowTable) {
      this.shadowTable = shadowTable;
   }

   public URI createNamespace(String namespace) 
      throws URISyntaxException
   {
      return shadowTable.createNamespace(namespace);
   }

   public Name createName(URI namespace,String localName) {
      if (localName.indexOf('{')>=0) {
         throw new IllegalArgumentException("Local names cannot contains '{' characters.");
      }
      return shadowTable.createName(namespace,localName);
   }

   public Name createName(String localName) {
      if (localName.indexOf('{')>=0) {
         throw new IllegalArgumentException("Local names cannot contains '{' characters.");
      }
      return shadowTable.createName(localName);
   }

   public ItemConstructor createItemConstructor() {
      return new MemoryItemConstructor(this,null);
   }
   
   public ItemConstructor createItemConstructor(URI baseURI) {
      return new MemoryItemConstructor(this,baseURI);
   }
   
}
